/*
* Copyright (c) 2010 Michael Collins
*
* This file is part of TerraFirma.
*
* TerraFirma is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TerraFirma is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TerraFirma.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "input_thread.h"
//#include "message_queue.h"
#include <boost/thread/thread.hpp>
#include <pthread.h>
#include <cstring>
#include <cstdio>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <SFML/Window.hpp>
#include "context.h"
//#include "logic_thread.h"
#include "message_logic.h"

#include <boost/serialization/vector.hpp>
#include <boost/serialization/map.hpp>

namespace engine
{
    //States has to have static linking. Probably bad form
    std::vector<input_thread::control_state> input_thread::states;
    std::vector<input_thread::control_rule> input_thread::rules;

    //Are these events of the same type (type and key)?
    static bool compare_events(const sf::Event& ev1, const sf::Event& ev2)
    {
        //if(ev1.Type != ev2.Type)
        //    return false;

        switch(ev1.Type)
        {
        case sf::Event::KeyPressed :
        case sf::Event::KeyReleased :
            if(ev2.Type != sf::Event::KeyPressed && ev2.Type != sf::Event::KeyReleased)
                return false;
            return (ev1.Key.Code == ev2.Key.Code);

        case sf::Event::MouseButtonPressed :
        case sf::Event::MouseButtonReleased :
            if(ev2.Type != sf::Event::MouseButtonPressed && ev2.Type != sf::Event::MouseButtonReleased)
                return false;
            return (ev1.MouseButton.Button == ev2.MouseButton.Button);

        case sf::Event::JoyButtonPressed :
        case sf::Event::JoyButtonReleased :
            if(ev2.Type != sf::Event::JoyButtonPressed && ev2.Type != sf::Event::JoyButtonReleased)
                return false;
            return (ev1.JoyButton.Button == ev2.JoyButton.Button);

        case sf::Event::MouseWheelMoved :
            if(ev2.Type != sf::Event::MouseWheelMoved)
                return false;
            return (ev1.MouseWheel.Delta * ev2.MouseWheel.Delta > 0); //True only when the signs match

        case sf::Event::MouseMoved :
            return (ev2.Type == sf::Event::MouseMoved);

        case sf::Event::JoyMoved :
            if(ev2.Type != sf::Event::JoyMoved)
                return false;
            return (ev1.JoyMove.Axis == ev2.JoyMove.Axis);

        default : return false;

        }
    }

    input_thread::~input_thread()
    {
        //dtor
    }

    void input_thread::F(const boost::thread::id tid)
    {
        //bool running = true;
        while (running)
        {
            //Sit in this tight loop if there is nothing to do
            boost::mutex dummy; //This is probably really stupid of me. Nobody really needs this mutex, we just want to be able to wake a condvar
            boost::unique_lock<boost::mutex> lock(dummy);

            //Wait 15 msec for a message to come into the queue
            if (msg_queue.isempty())
                msg_queue.read.timed_wait (lock, boost::posix_time::milliseconds(30));

            //Either the 15 msec is up with no pending messages,
            //or there's a message waiting for us in the queue
            //(We could have been randomly woken as well. Whatever.)
            while (!msg_queue.isempty())
            {
                process_message(msg_queue.front());
                msg_queue.pop();
            }

            //Get control events
            sf::Event ev;
            while (context::Instance().main_window.GetEvent(ev))
                process_event(ev);
        }

    };

    void input_thread::process_message(const control_msg& msg)
    {
        //Process the incoming message from another host or thread
        if(msg.header().get_type() == BCAST_CLOSE)
            running = false;
    }

    //static const task_id logic_tid = get_task_id_by_name("logic_thread");

    void input_thread::bool_event(const control_rule& ctrl, bool keystate)
    {
        Player_Control_Message ctrl_msg;
        if(ctrl.type == BUTN_MOM) {
            if(ctrl.state->st_b.state == keystate)
                return; //No change. Stupid repeating keys
            ctrl.state->st_b.state = keystate;
            printf("%s %s\n", ctrl.name, (keystate)? "ON" : "OFF");
            //Barf a message to the game queue
            ctrl_msg.control_id = ctrl.state->st_b.idx;
            ctrl_msg.type = Player_Control_Message::BINARY;
            ctrl_msg.state.binary = keystate;
            MPI.enqueue_shared(&ctrl_msg.header);
            return;
        } else if (ctrl.type == BUTN_TOG) {
            if(keystate) return; //Toggle is only sensitive to releases
            ctrl.state->st_b.state = !ctrl.state->st_b.state;
            //Barf a message to the game queue
            ctrl_msg.control_id = ctrl.state->st_b.idx;
            ctrl_msg.type = Player_Control_Message::BINARY;
            ctrl_msg.state.binary = keystate;
            MPI.enqueue_shared(&ctrl_msg.header);
            return;
        } else if (ctrl.type == BUTN_UP) {
            if(keystate && ctrl.state->st_2d.state_y == S16_MAX)
                return; //No change. Stupid repeating keys
            ctrl.state->st_2d.state_y = (keystate)? S16_MAX : 0;
            //Barf a message to the game queue
            ctrl_msg.control_id = ctrl.state->st_b.idx;
            ctrl_msg.type = Player_Control_Message::ANALOG_2D;
            ctrl_msg.state.analog.x = ctrl.state->st_2d.state_x;
            ctrl_msg.state.analog.y = ctrl.state->st_2d.state_y;
            MPI.enqueue_shared(&ctrl_msg.header);
            //return;
        } else if (ctrl.type == BUTN_DWN) {
            if(keystate && ctrl.state->st_2d.state_y == -S16_MAX)
                return; //No change. Stupid repeating keys
            ctrl.state->st_2d.state_y = (keystate)? -S16_MAX : 0;
            //Barf a message to the game queue
            ctrl_msg.control_id = ctrl.state->st_b.idx;
            ctrl_msg.type = Player_Control_Message::ANALOG_2D;
            ctrl_msg.state.analog.x = ctrl.state->st_2d.state_x;
            ctrl_msg.state.analog.y = ctrl.state->st_2d.state_y;
            MPI.enqueue_shared(&ctrl_msg.header);
            //return;
        } else if (ctrl.type == BUTN_LFT) {
            if(keystate && ctrl.state->st_2d.state_x == -S16_MAX)
                return; //No change. Stupid repeating keys
            ctrl.state->st_2d.state_x = (keystate)? -S16_MAX : 0;
            //Barf a message to the game queue
            ctrl_msg.control_id = ctrl.state->st_b.idx;
            ctrl_msg.type = Player_Control_Message::ANALOG_2D;
            ctrl_msg.state.analog.x = ctrl.state->st_2d.state_x;
            ctrl_msg.state.analog.y = ctrl.state->st_2d.state_y;
            MPI.enqueue_shared(&ctrl_msg.header);
            //return;
        } else if (ctrl.type == BUTN_RGT) {
            if(keystate && ctrl.state->st_2d.state_x == S16_MAX)
                return; //No change. Stupid repeating keys
            ctrl.state->st_2d.state_x = (keystate)? S16_MAX : 0;
            //Barf a message to the game queue
            ctrl_msg.control_id = ctrl.state->st_b.idx;
            ctrl_msg.type = Player_Control_Message::ANALOG_2D;
            ctrl_msg.state.analog.x = ctrl.state->st_2d.state_x;
            ctrl_msg.state.analog.y = ctrl.state->st_2d.state_y;
            MPI.enqueue_shared(&ctrl_msg.header);
            //return;
        }

        //Debug out the position of the control
        //if(ctrl.type >= BUTN_UP) {
            int16_t x, y;
            x = ctrl.state->st_2d.state_x;
            y = ctrl.state->st_2d.state_y;
            printf("%s -- ", ctrl.name);
            if(y == -S16_MAX)
                printf("S ");
            else if(y == S16_MAX)
                printf("N ");
            else
                printf("%d ", y);

            if(x == -S16_MAX)
                printf("W\n");
            else if(x == S16_MAX)
                printf("E\n");
            else
                printf("%d\n", x);
        //}
    }

    void input_thread::mouse_event(const control_rule& ctrl, const sf::Event::MouseMoveEvent& ev)
    {
        ctrl.state->st_2d.delt_x += ev.X * ctrl.sensitivity;
        ctrl.state->st_2d.delt_y += ev.Y * ctrl.sensitivity;
        ctrl.state->st_2d.state_x += ctrl.state->st_2d.delt_x;
        ctrl.state->st_2d.state_y += ctrl.state->st_2d.delt_y;
        //Barf a message to the game queue here
    }

    void input_thread::joyaxis_event(const control_rule& ctrl, float pos)
    {
        if(ctrl.state->type == STATE_2D) {
            if(ctrl.type == JOYSTICK_X)
                ctrl.state->st_2d.state_x = pos * ctrl.sensitivity;
            else if(ctrl.type == JOYSTICK_Y)
                ctrl.state->st_2d.state_y = pos * ctrl.sensitivity;
            else //Inverted y
                ctrl.state->st_2d.state_y = -pos * ctrl.sensitivity;
        } else
            //1D control (like a throttle or steering wheel something)
            ctrl.state->st_1d.state = ctrl.sensitivity * (ctrl.type == JOYSTICK_INV_Y)? -pos : pos;
    }

    void input_thread::text_event(const sf::Event::TextEvent ev)
    {
        /*
        unsigned char c = (unsigned char) ev.Unicode;
        if(c == '\r' || c == '\n') {
            text_box->submit();
            text_box->clear();
            text_box = NULL;
        } else if (c == 255) {
            text_box->clear(); //Clear out any text the user may have entered
            text_box = NULL;
        } else {
            //Just a regular text key
            text_box->putc(c);
        }
        */
    }

    void input_thread::process_event(const sf::Event& ev)
    {
        control_rule* rule;
        //Process the detected event from the window manager
        switch (ev.Type)
        {
        case sf::Event::Closed :
            printf("Ok, goodbye!\n");
            //running = false;
            MPI.broadcast_close();
            break;
        case sf::Event::Resized :
            //Send a window resize event to the video thread
            break;
        case sf::Event::GainedFocus :
            has_focus = true; break;
        case sf::Event::LostFocus :
            has_focus = false; break;
        case sf::Event::KeyPressed :
        case sf::Event::KeyReleased :
            if(has_focus && !text_box)
            {
                if(get_control_rule(ev, &rule))
                    bool_event(*rule, (ev.Type == sf::Event::KeyPressed));
            }
            break;
        case sf::Event::TextEntered :
            //printf("Some text was entered:%x Tell me if a key was pressed!\n", ev.Text.Unicode);
            //Pop the key press event off the stack so we don't
            break;
        case sf::Event::MouseButtonPressed :
        case sf::Event::MouseButtonReleased :
            if(has_focus)
            {
                if(get_control_rule(ev, &rule))
                    bool_event(*rule, (ev.Type == sf::Event::MouseButtonPressed));
            }
            break;
        case sf::Event::JoyButtonPressed :
        case sf::Event::JoyButtonReleased :
            if(has_focus)
            {
                if(get_control_rule(ev, &rule))
                    bool_event(*rule, (ev.Type == sf::Event::JoyButtonPressed));
            }
            break;
        case sf::Event::MouseMoved :
            if(has_focus)
            {
                if(mouse_rule)
                    mouse_event(*mouse_rule.rule, ev.MouseMove);
            }
            break;
        case sf::Event::MouseWheelMoved :
            if(has_focus)
            {
                if((ev.MouseWheel.Delta > 0) && (wheel_up)) {
                    bool_event(*(wheel_up.rule), true);
                    wheel_up.rule->state->st_b.state = false; //There's no analogue for momentary
                }
                else if ((ev.MouseWheel.Delta < 0) && (wheel_down)) {
                    bool_event(*(wheel_down.rule), true);
                    wheel_down.rule->state->st_b.state = false; //There's no analogue for momentary
                }
            }
            break;
        case sf::Event::JoyMoved :
            if(has_focus)
            {
                if(get_control_rule(ev, &rule))
                    joyaxis_event(*rule, ev.JoyMove.Position);
            }
            break;
        case sf::Event::MouseLeft :
            if(has_focus)
            {
                //We should return the mouse to the center
                sf::Window& window = context::Instance().main_window;
                window.SetCursorPosition(window.GetWidth()>>1, window.GetHeight()>>1);
            }
            break;
        default : break; //do nothing!!
        }
    }//process_event

    /** Manipulation functions -- Alter on a per-mod basis how control works **/
    //Undefined results if there are more than 256 control states (this is a lot, nonetheless. Even for a mech game)
    control_idx input_thread::add_boolean_state(control_idx num)
    {
        if (!num)
            return 0; //Return 0 for stupid arguments.

        //proto state to copy repeatedly into the pool
        control_state proto;
        proto.st_b.type = STATE_BOOLEAN;
        proto.st_b.state = 0; //off, hopefully
        proto.st_b.idx = states.size();

        control_idx retval = states.size();

        for (uint8_t i = 0; i < num; i++) {
            states.push_back(proto);
            proto.st_b.idx++;
        }
        return retval;
    }

    control_idx input_thread::add_1d_state(control_idx num)
    {
        if (!num)
            return 0; //Return 0 for stupid arguments.

        //proto state to copy repeatedly into the pool
        control_state proto;
        proto.st_1d.type = STATE_1D;
        proto.st_1d.state = proto.st_1d.delt = 0;
        proto.st_1d.idx = states.size();

        control_idx retval = states.size();

        for (uint8_t i = 0; i < num; i++) {
            states.push_back(proto);
            proto.st_1d.idx++;
        }
        return retval;
    }

    control_idx input_thread::add_2d_state(control_idx num)
    {
        if (!num)
            return 0; //Return 0 for stupid arguments.

        //proto state to copy repeatedly into the pool
        control_state proto;
        proto.st_2d.type = STATE_2D;
        proto.st_2d.state_x = proto.st_2d.state_y = 0;
        proto.st_2d.delt_x = proto.st_2d.delt_y = 0;
        proto.st_2d.idx = states.size();

        control_idx retval = states.size();

        for (uint8_t i = 0; i < num; i++) {
            states.push_back(proto);
            proto.st_2d.idx++;
        }
        return retval;
    }

    bool input_thread::add_control_rule(const char* name, control_idx idx, control_modifier_type type, float sens)
    {
        sf::Event default_ev;
        default_ev.Type = sf::Event::Closed;
        return add_control_rule(name, idx, type, sens, default_ev);
    }

    bool input_thread::add_control_rule(const char* name, control_idx idx, control_modifier_type type, float sens, const sf::Event& dflt)
    {
        //Sanity checks!!
        if (!name)
            return false; //It needs a name

        if (idx >= states.size())
            return false; //Invalid control index

        switch (dflt.Type)
        {
            //These are all bogus events!
            //case sf::Event::Closed : //We will leave this in for "UNBOUND" events
        case sf::Event::Resized :
        case sf::Event::LostFocus :
        case sf::Event::GainedFocus :
        case sf::Event::TextEntered :
        case sf::Event::MouseEntered :
        case sf::Event::MouseLeft :
            return false;
        default:
            break;
        }

        //Now we dig deeper to make sure the rule type the state type, and the event agree
        if (type == BUTN_MOM || type == BUTN_TOG)
        {
            //These are purely binary switches. They refer only to boolean events
            if (states[idx].type != STATE_BOOLEAN)
                return false;

            //Add the rule to the pool!
            rules.push_back(control_rule());
            control_rule& rule = rules.back();
            //rule.name = name;
            strncpy(rule.name, name, 31); rule.name[32] = 0; //Null terminate
            rule.state = &(states[idx]);
            rule.type = type;
            rule.sensitivity = 1.0f;
            rule.Secondary.Type = sf::Event::Closed; //Unbound

            //Bind default button event to this. If not a button event, it is considered an unbound rule
            switch (dflt.Type)
            {
            case sf::Event::KeyPressed :
            case sf::Event::KeyReleased :
            case sf::Event::MouseWheelMoved :
            case sf::Event::MouseButtonPressed :
            case sf::Event::MouseButtonReleased :
            case sf::Event::JoyButtonPressed :
            case sf::Event::JoyButtonReleased :
                bind_control(dflt, &rule, PRIMARY);
                break;
            default :
                rule.Primary.Type = sf::Event::Closed; //Unbound
            }
            return true; //All done!
        }
        else
        {
            //This refers to an analog thing
            if (states[idx].type == STATE_BOOLEAN)
                return false;

            //Mice can only control 2D controls (change this?)
            if (states[idx].type == STATE_1D && (type == MOUSE || type == MOUSE_INV))
                return false;

            //Add the rule to the pool!
            rules.push_back(control_rule());
            control_rule& rule = rules.back();
            //rule.name = name;
            strncpy(rule.name, name, 31); rule.name[32] = 0; //Null terminate
            rule.state = &(states[idx]);
            rule.type = type;
            rule.sensitivity = sens;
            rule.Secondary.Type = sf::Event::Closed; //Unbound

            switch (dflt.Type)
            {
            case sf::Event::KeyPressed :
            case sf::Event::KeyReleased :
            case sf::Event::MouseWheelMoved :
            case sf::Event::MouseButtonPressed :
            case sf::Event::MouseButtonReleased :
            case sf::Event::JoyButtonPressed :
            case sf::Event::JoyButtonReleased :
            case sf::Event::MouseMoved :
            case sf::Event::JoyMoved :
                bind_control(dflt, &rule, PRIMARY);
                break;
            default :
                rule.Primary.Type = sf::Event::Closed; //Unbound
            }
            return true;
        }
    }

    bool input_thread::bind_control(const sf::Event& ev, control_rule* rule, uint8_t slot)
    {
        if (!rule) return false;

        if (slot > SECONDARY) return false;

        switch (ev.Type)
        {
            //Switch events
        case sf::Event::KeyPressed :
        case sf::Event::KeyReleased :
            if (rule->type <= BUTN_RGT)
            {
                unbind_control(ev);
                keymap[ev.Key.Code] = control_rule_ref(rule);
                //Put the event in the rule's slot as an example (mostly for printing out)
                ((slot == PRIMARY)? rule->Primary : rule->Secondary) = ev;
                return true;
            }
            return false;
            break;

        case sf::Event::MouseButtonPressed :
        case sf::Event::MouseButtonReleased :
            if (rule->type <= BUTN_RGT)
            {
                unbind_control(ev);
                mousebtnmap[ev.MouseButton.Button] = control_rule_ref(rule);
                //Put the event in the rule's slot as an example (mostly for printing out)
                ((slot == PRIMARY)? rule->Primary : rule->Secondary) = ev;
                return true;
            }
            return false;
            break;

        case sf::Event::JoyButtonPressed :
        case sf::Event::JoyButtonReleased :
            if (rule->type <= BUTN_RGT)
            {
                unbind_control(ev);
                joybtnmap[ev.JoyButton.Button] = control_rule_ref(rule);
                //Put the event in the rule's slot as an example (mostly for printing out)
                ((slot == PRIMARY)? rule->Primary : rule->Secondary) = ev;
                return true;
            }
            return false;
            break;

        case sf::Event::MouseWheelMoved:
            if (rule->type <= BUTN_RGT)
            {
                unbind_control(ev);
                ((ev.MouseWheel.Delta > 0)? wheel_up : wheel_down) = rule;
                //Put the event in the rule's slot as an example (mostly for printing out)
                ((slot == PRIMARY)? rule->Primary : rule->Secondary) = ev;
                return true;
            }
            return false;
            break;

            //Analog events
        case sf::Event::MouseMoved:
            if (rule->type >= MOUSE)
            {
                unbind_control(ev);
                mouse_rule = rule;
                //Put the event in the rule's slot as an example (mostly for printing out)
                ((slot == PRIMARY)? rule->Primary : rule->Secondary) = ev;
                return true;
            }
            return false;
            break;

        case sf::Event::JoyMoved:
            if (rule->type == JOYSTICK_X || rule->type == JOYSTICK_Y || rule->type == JOYSTICK_INV_Y)
            {
                unbind_control(ev);
                joyaxismap[ev.JoyMove.Axis] = control_rule_ref(rule);
                //Put the event in the rule's slot as an example (mostly for printing out)
                ((slot == PRIMARY)? rule->Primary : rule->Secondary) = ev;
                return true;
            }
            return false;
            break;

        default :
            return false; //What did you do, you dumbass?
        }
    }

    bool input_thread::get_control_rule(const char* name, control_rule** rule)
    {
        //Iterate over all the rules
        for (std::vector<control_rule>::iterator it = rules.begin(); it < rules.end(); it++)
        {
            if (strcmp((*it).name, name) == 0)
            {
                //Found it!
                (*rule) = &(*it); //I THINK the iterator has an overloaded dereference operator
                return true;
            }
        }
        return false; //We didn't find shit.
    }

    bool input_thread::get_control_rule(const sf::Event& ev, control_rule** rule)
    {
        //We must declare all the vars we may need outside of the switch
        std::map<sf::Key::Code, control_rule_ref>::const_iterator key_it;
        std::map<sf::Mouse::Button, control_rule_ref>::const_iterator mbt_it;
        std::map<sf::Joy::Axis, control_rule_ref>::const_iterator jax_it;
        std::map<unsigned int, control_rule_ref>::const_iterator jbt_it;

        //ev may map to a control rule. First, determine what kind of event ev is
        switch (ev.Type)
        {
        case sf::Event::KeyPressed :
        case sf::Event::KeyReleased :
            //This is a keypress
            key_it = keymap.find(ev.Key.Code);
            if (key_it == keymap.end())
                return false; //Unmapped
            else
            {
                (*rule) = (*key_it).second.rule;
                return true;
            }

        case sf::Event::MouseButtonPressed :
        case sf::Event::MouseButtonReleased :
            //This is a mouse button event
            mbt_it = mousebtnmap.find(ev.MouseButton.Button);
            if (mbt_it == mousebtnmap.end())
                return false;
            else
            {
                (*rule) = (*mbt_it).second.rule;
                return true;
            }

        case sf::Event::JoyButtonPressed :
        case sf::Event::JoyButtonReleased :
            //This is a joystick button event
            jbt_it = joybtnmap.find(ev.JoyButton.Button);
            if (jbt_it == joybtnmap.end())
                return false;
            else
            {
                (*rule) = (*jbt_it).second.rule;
                return true;
            }

        case sf::Event::MouseWheelMoved :
            if(ev.MouseWheel.Delta > 0)
            {
                if(wheel_up) {
                    (*rule) = wheel_up; return true;
                } else
                    return false;
            } else {
                if(wheel_down) {
                    (*rule) = wheel_down.rule; return true;
                } else
                    return false;
            }

        case sf::Event::JoyMoved :
            //This is a joystick axis event
            jax_it = joyaxismap.find(ev.JoyMove.Axis);
            if (jax_it == joyaxismap.end())
                return false;
            else {
                (*rule) = (*jax_it).second.rule;
                return true;
            }

        case sf::Event::MouseMoved :
            //This is a mouse movement event
            if(mouse_rule) {
                (*rule) = mouse_rule.rule;
                return true;
            } else
                return false;

        default :
            //This is some window-management event like close or resize. Ignore it
            return false;
        }
    }

    bool input_thread::unbind_control(const sf::Event& ev)
    {
        control_rule* rule;
        sf::Event null_ev;
        null_ev.Type = sf::Event::Closed;

        if(!get_control_rule(ev, &rule))
            return false; //This event is unbound already

        if(compare_events(rule->Primary, ev))
            rule->Primary = null_ev; //Erase to add a null event
        else
            rule->Secondary = null_ev; //Perhaps this is shitty. Blindly erase the secondary proto-event
        return true;
    }

    void input_thread::save_fixed(boost::archive::xml_oarchive& oa) const
    {
        oa & BOOST_SERIALIZATION_NVP(states);
        oa & BOOST_SERIALIZATION_NVP(rules);
    }

    void input_thread::load_fixed(boost::archive::xml_iarchive& ia)
    {
        ia & BOOST_SERIALIZATION_NVP(states);
        ia & BOOST_SERIALIZATION_NVP(rules);
    }

    void input_thread::save_bindings(boost::archive::xml_oarchive& oa) const
    {
        oa & BOOST_SERIALIZATION_NVP(keymap);
        oa & BOOST_SERIALIZATION_NVP(mousebtnmap);
        oa & BOOST_SERIALIZATION_NVP(joyaxismap);
        oa & BOOST_SERIALIZATION_NVP(joybtnmap);
        oa & BOOST_SERIALIZATION_NVP(mouse_rule);
        oa & BOOST_SERIALIZATION_NVP(wheel_down);
        oa & BOOST_SERIALIZATION_NVP(wheel_up);
    }

    void input_thread::load_bindings(boost::archive::xml_iarchive& ia)
    {
        ia & BOOST_SERIALIZATION_NVP(keymap);
        ia & BOOST_SERIALIZATION_NVP(mousebtnmap);
        ia & BOOST_SERIALIZATION_NVP(joyaxismap);
        ia & BOOST_SERIALIZATION_NVP(joybtnmap);
        ia & BOOST_SERIALIZATION_NVP(mouse_rule);
        ia & BOOST_SERIALIZATION_NVP(wheel_down);
        ia & BOOST_SERIALIZATION_NVP(wheel_up);
    }

    //String that names the key or button pressed
    static const char* event_to_name(const sf::Event& ev)
    {
        //hash table (perhaps do some bounds checking later on so the player doesn't crash the game
        //by using a giant fucking joystick with a billion buttons?
        const char* button_text[] =
        {
            "JOYSTICK BUTTON0",
            "JOYSTICK BUTTON1",
            "JOYSTICK BUTTON2",
            "JOYSTICK BUTTON3",
            "JOYSTICK BUTTON4",
            "JOYSTICK BUTTON5",
            "JOYSTICK BUTTON6",
            "JOYSTICK BUTTON7",
            "JOYSTICK BUTTON8",
            "JOYSTICK BUTTON9",
            "JOYSTICK BUTTON10",
            "JOYSTICK BUTTON11",
            "JOYSTICK BUTTON12",
            "JOYSTICK BUTTON13",
            "JOYSTICK BUTTON14",
            "JOYSTICK BUTTON15",
            "JOYSTICK BUTTON16",
            "JOYSTICK BUTTON17",
            "JOYSTICK BUTTON18",
            "JOYSTICK BUTTON19",
            "JOYSTICK BUTTON20",
            "JOYSTICK BUTTON21",
            "JOYSTICK BUTTON22",
            "JOYSTICK BUTTON23",
            "JOYSTICK BUTTON24",
            "JOYSTICK BUTTON25",
            "JOYSTICK BUTTON26",
            "JOYSTICK BUTTON27",
            "JOYSTICK BUTTON28",
            "JOYSTICK BUTTON29",
            "JOYSTICK BUTTON30",
            "JOYSTICK BUTTON31"
        };
        switch (ev.Type)
        {
            //Keyboard
        case sf::Event::KeyPressed:
        case sf::Event::KeyReleased:
            switch (ev.Key.Code)
            {
            case sf::Key::A :
                return "A";
            case sf::Key::B :
                return "B";
            case sf::Key::C :
                return "C";
            case sf::Key::D :
                return "D";
            case sf::Key::E :
                return "E";
            case sf::Key::F :
                return "F";
            case sf::Key::G :
                return "G";
            case sf::Key::H :
                return "H";
            case sf::Key::I :
                return "I";
            case sf::Key::J :
                return "J";
            case sf::Key::K :
                return "K";
            case sf::Key::L :
                return "L";
            case sf::Key::M :
                return "M";
            case sf::Key::N :
                return "N";
            case sf::Key::O :
                return "O";
            case sf::Key::P :
                return "P";
            case sf::Key::Q :
                return "Q";
            case sf::Key::R :
                return "R";
            case sf::Key::S :
                return "S";
            case sf::Key::T :
                return "T";
            case sf::Key::U :
                return "U";
            case sf::Key::V :
                return "V";
            case sf::Key::W :
                return "W";
            case sf::Key::X :
                return "X";
            case sf::Key::Y :
                return "Y";
            case sf::Key::Z :
                return "Z";
            case sf::Key::Num0 :
                return "0";
            case sf::Key::Num1 :
                return "1";
            case sf::Key::Num2 :
                return "2";
            case sf::Key::Num3 :
                return "3";
            case sf::Key::Num4 :
                return "4";
            case sf::Key::Num5 :
                return "5";
            case sf::Key::Num6 :
                return "6";
            case sf::Key::Num7 :
                return "7";
            case sf::Key::Num8 :
                return "8";
            case sf::Key::Num9 :
                return "9";
            case sf::Key::Escape :
                return "ESC";
            case sf::Key::LControl :
                return "LEFT CTRL";
            case sf::Key::LShift :
                return "LEFT SHIFT";
            case sf::Key::LAlt :
                return "LEFT ALT";
            case sf::Key::LSystem :
                return "LEFT SYSTEM";
            case sf::Key::RControl :
                return "RIGHT CTRL";
            case sf::Key::RShift :
                return "RIGHT SHIFT";
            case sf::Key::RAlt :
                return "RIGHT ALT";
            case sf::Key::RSystem :
                return "RIGHT SYSTEM";
            case sf::Key::Menu :
                return "MENU";
            case sf::Key::LBracket :
                return "LEFT BRACKET";
            case sf::Key::RBracket :
                return "RIGHT BRACKET";
            case sf::Key::SemiColon :
                return ";";
            case sf::Key::Comma :
                return ",";
            case sf::Key::Period :
                return ".";
            case sf::Key::Quote :
                return "\'";
            case sf::Key::Slash :
                return "/";
            case sf::Key::BackSlash :
                return "\\";
            case sf::Key::Tilde :
                return "`";
            case sf::Key::Equal :
                return "=";
            case sf::Key::Dash :
                return "-"; //Ambiguous with subtract
            case sf::Key::Space :
                return "SPACEBAR";
            case sf::Key::Return :
                return "ENTER";
            case sf::Key::Back :
                return "BACKSPACE";
            case sf::Key::Tab :
                return "TAB";
            case sf::Key::PageUp :
                return "PAGE UP";
            case sf::Key::PageDown :
                return "PAGE DOWN";
            case sf::Key::End :
                return "END";
            case sf::Key::Home :
                return "HOME";
            case sf::Key::Insert :
                return "INSERT";
            case sf::Key::Delete :
                return "DELETE";
            case sf::Key::Add :
                return "+";
            case sf::Key::Subtract :
                return "-"; //Ambiguous with dash. This is the numpad version
            case sf::Key::Multiply :
                return "*";
            case sf::Key::Divide :
                return "/"; //Ambiguous with slash. This is the numpad version
            case sf::Key::Left :
                return "LEFT";
            case sf::Key::Right :
                return "RIGHT";
            case sf::Key::Up :
                return "UP";
            case sf::Key::Down :
                return "DOWN";
            case sf::Key::Numpad0 :
                return "NUMPAD 0";
            case sf::Key::Numpad1 :
                return "NUMPAD 1";
            case sf::Key::Numpad2 :
                return "NUMPAD 2";
            case sf::Key::Numpad3 :
                return "NUMPAD 3";
            case sf::Key::Numpad4 :
                return "NUMPAD 4";
            case sf::Key::Numpad5 :
                return "NUMPAD 5";
            case sf::Key::Numpad6 :
                return "NUMPAD 6";
            case sf::Key::Numpad7 :
                return "NUMPAD 7";
            case sf::Key::Numpad8 :
                return "NUMPAD 8";
            case sf::Key::Numpad9 :
                return "NUMPAD 9";
            case sf::Key::F1 :
                return "F1";
            case sf::Key::F2 :
                return "F2";
            case sf::Key::F3 :
                return "F3";
            case sf::Key::F4 :
                return "F4";
            case sf::Key::F5 :
                return "F5";
            case sf::Key::F6 :
                return "F6";
            case sf::Key::F7 :
                return "F7";
            case sf::Key::F8 :
                return "F8";
            case sf::Key::F9 :
                return "F9";
            case sf::Key::F10 :
                return "F10";
            case sf::Key::F11 :
                return "F11";
            case sf::Key::F12 :
                return "F12";
            case sf::Key::F13 :
                return "F13";
            case sf::Key::F14 :
                return "F14";
            case sf::Key::F15 :
                return "F15";
            case sf::Key::Pause :
                return "PAUSE";
            default :
                return "UNDEFINED KEY";
            }
            //Mouse Button
        case sf::Event::MouseButtonPressed:
        case sf::Event::MouseButtonReleased:
            switch (ev.MouseButton.Button)
            {
            case sf::Mouse::Left :
                return "MOUSE LEFT CLICK";
            case sf::Mouse::Right :
                return "MOUSE RIGHT CLICK";
            case sf::Mouse::Middle :
                return "MOUSE MIDDLE CLICK";
            case sf::Mouse::XButton1 :
                return "MOUSE X1 CLICK"; //Anyone know a better name? I'm all ears.
            case sf::Mouse::XButton2 :
                return "MOUSE X2 CLICK";
            default :
                return "UNDEFINED MOUSE BUTTON";
            }
            //Joystick (gamepad) Button
        case sf::Event::JoyButtonPressed:
        case sf::Event::JoyButtonReleased:
            return button_text[ev.JoyButton.Button];
            //Mousewheel
        case sf::Event::MouseWheelMoved:
            if (ev.MouseWheel.Delta > 0) return "MOUSE WHEEL UP";
            else return "MOUSE WHEEL DOWN";
            //Mouse
        case sf::Event::MouseMoved:
            return "MOUSE";
            //Joystick
        case sf::Event::JoyMoved:
            switch (ev.JoyMove.Axis)
            {
            case sf::Joy::AxisX :
                return "JOY X";
            case sf::Joy::AxisY :
                return "JOY Y";
            case sf::Joy::AxisZ :
                return "JOY Z";
            case sf::Joy::AxisR :
                return "JOY R";
            case sf::Joy::AxisU :
                return "JOY U";
            case sf::Joy::AxisV :
                return "JOY V";
            case sf::Joy::AxisPOV :
                return "JOY POV";
            default :
                return "UNBOUND AXIS";
            }
        default:
            return "UNBOUND EVENT";
        }
    }
};
namespace boost {
namespace serialization {

    //Serialize the sf::Event class
    template<class Archive>
    void serialize(Archive& ar, sf::Event& ev, const unsigned int version)
    {
        ar & make_nvp("Type", ev.Type);
        switch(ev.Type)
        {
        case sf::Event::KeyPressed :
        case sf::Event::KeyReleased :
            ar & make_nvp("Arg", ev.Key.Code); break;
        case sf::Event::MouseButtonPressed :
        case sf::Event::MouseButtonReleased :
            ar & make_nvp("Arg", ev.MouseButton.Button); break;
        case sf::Event::JoyButtonPressed :
        case sf::Event::JoyButtonReleased :
            ar & make_nvp("Arg", ev.JoyButton.Button); break;
        case sf::Event::JoyMoved :
            ar & make_nvp("Arg", ev.JoyMove.Axis); break;
        case sf::Event::MouseWheelMoved :
            ar & make_nvp("Arg", ev.MouseWheel.Delta); break;
        default : break;
        }
    }
}
}
